//  SMPSO.java
//
//  Author:
//       Antonio J. Nebro <antonio@lcc.uma.es>
//       Juan J. Durillo <durillo@lcc.uma.es>
//
//  Copyright (c) 2011 Antonio J. Nebro, Juan J. Durillo
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Lesser General Public License for more details.
// 
//  You should have received a copy of the GNU Lesser General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.

package hidra.many.metaheuristics.approaches.NADIRMOPSOv2bkp;

import hidra.core.population.HIDRAPopulationAlgorithm;
import hidra.experiments.GlobalSettings;
import hidra.jmetal.core.Operator;
import hidra.jmetal.core.Problem;
import hidra.jmetal.core.Solution;
import hidra.jmetal.core.SolutionSet;
import hidra.qualityIndicator.Hypervolume;
import hidra.qualityIndicator.QualityIndicator;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

import jmetal.operators.crossover.Crossover;
import jmetal.operators.crossover.CrossoverFactory;
import jmetal.util.Distance;
import jmetal.util.JMException;
import jmetal.util.PseudoRandom;
import jmetal.util.archive.CrowdingArchive;
import jmetal.util.comparators.CrowdingDistanceComparator;
import jmetal.util.comparators.DominanceComparator;
import jmetal.util.comparators.FitnessComparator;
import jmetal.util.comparators.ObjectiveComparator;
import jmetal.util.wrapper.XReal;

/**
 * This class implements the SMPSO algorithm described in:
 * A.J. Nebro, J.J. Durillo, J. Garcia-Nieto, C.A. Coello Coello, F. Luna and E. Alba
 * "SMPSO: A New PSO-based Metaheuristic for Multi-objective Optimization". 
 * IEEE Symposium on Computational Intelligence in Multicriteria Decision-Making 
 * (MCDM 2009), pp: 66-73. March 2009
 */
public class SUBSMPSO extends HIDRAPopulationAlgorithm {

  /**
   * Stores the number of particles_ used
   */
  private int swarmSize_;
  /**
   * Stores the maximum size for the archive
   */
  private int archiveSize_;
  /**
   * Stores the maximum number of iteration_
   */
 // private int maxIterations_;
  /**
   * Stores the current number of iteration_
   */
  //private int iteration_;
  /**
   * Stores the particles
   */
  private SolutionSet particles_;
  /**
   * Stores the best_ solutions founds so far for each particles
   */
  private Solution[] best_;
  /**
   * Stores the leaders_
   */
  private ModifiedCrowdingArchive leaders_;
  /**
   * Stores the speed_ of each particle
   */
  private double[][] speed_;
  /**
   * Stores a comparator for checking dominance
   */
  private Comparator dominance_;
  /**
   * Stores a comparator for crowding checking
   */
  private Comparator crowdingDistanceComparator_;
  /**
   * Stores a <code>Distance</code> object
   */
  private Distance distance_;
  /**
   * Stores a operator for non uniform mutations
   */
  private Operator polynomialMutation_;

  QualityIndicator indicators_; // QualityIndicator object

  private double[] ideal;
  private int index = 0;
  private double bestValueKnowed = Double.POSITIVE_INFINITY;
  
  
  double r1Max_;
  double r1Min_;
  double r2Max_;
  double r2Min_;
  double C1Max_;
  double C1Min_;
  double C2Max_;
  double C2Min_;
  double WMax_;
  double WMin_;
  double ChVel1_;
  double ChVel2_;

  private double trueHypervolume_;
  private Hypervolume hy_;
  private SolutionSet trueFront_;
  private double deltaMax_[];
  private double deltaMin_[];
  boolean success_;
  
  /** 
   * Constructor
   * @param problem Problem to solve
   */
  public SUBSMPSO(Problem problem) {
    super(problem) ;

    r1Max_ = 1.0;
    r1Min_ = 0.0;
    r2Max_ = 1.0;
    r2Min_ = 0.0;
    C1Max_ = 2.5;
    C1Min_ = 1.5;
    C2Max_ = 2.5;
    C2Min_ = 1.5;
    WMax_ = 0.1;
    WMin_ = 0.1;
    ChVel1_ = -1;
    ChVel2_ = -1;
  } // Constructor

  public void setIndex(int index){
	  this.index = index;
  }
  
  public SUBSMPSO(Problem problem,
    Vector<Double> variables,
    String trueParetoFront) throws FileNotFoundException {
    super(problem) ;

    r1Max_ = variables.get(0);
    r1Min_ = variables.get(1);
    r2Max_ = variables.get(2);
    r2Min_ = variables.get(3);
    C1Max_ = variables.get(4);
    C1Min_ = variables.get(5);
    C2Max_ = variables.get(6);
    C2Min_ = variables.get(7);
    WMax_ = variables.get(8);
    WMin_ = variables.get(9);
    ChVel1_ = variables.get(10);
    ChVel2_ = variables.get(11);

    hy_ = new Hypervolume();
    hidra.qualityIndicator.util.MetricsUtil mu = new hidra.qualityIndicator.util.MetricsUtil();
    trueFront_ = mu.readNonDominatedSolutionSet(trueParetoFront);
    trueHypervolume_ = hy_.hypervolume(trueFront_.writeObjectivesToMatrix(),
      trueFront_.writeObjectivesToMatrix(),
      problem_.getNumberOfObjectives());

  } // SMPSO
  

  /** 
   * Constructor
   * @param problem Problem to solve
   */
  public SUBSMPSO(Problem problem, String trueParetoFront) throws FileNotFoundException {
    super(problem) ;
    hy_ = new Hypervolume();
    hidra.qualityIndicator.util.MetricsUtil mu = new hidra.qualityIndicator.util.MetricsUtil();
    trueFront_ = mu.readNonDominatedSolutionSet(trueParetoFront);
    trueHypervolume_ = hy_.hypervolume(trueFront_.writeObjectivesToMatrix(),
      trueFront_.writeObjectivesToMatrix(),
      problem_.getNumberOfObjectives());

    // Default configuration
    r1Max_ = 1.0;
    r1Min_ = 0.0;
    r2Max_ = 1.0;
    r2Min_ = 0.0;
    C1Max_ = 2.5;
    C1Min_ = 1.5;
    C2Max_ = 2.5;
    C2Min_ = 1.5;
    WMax_ = 0.1;
    WMin_ = 0.1;
    ChVel1_ = -1;
    ChVel2_ = -1;
  } // Constructor

  /**
   * Initialize all parameter of the algorithm
   */
  public void initParams() {
    swarmSize_ = 30;
    archiveSize_ = 50;
    

    indicators_ = (QualityIndicator) getInputParameter("indicators");

    polynomialMutation_ = operators_.get("mutation") ; 

    archiveSize_ = 2 * archiveSize_;
    
    iteration_ = 0 ;

    
    
    success_ = false;

    ideal = new double[problem_.getNumberOfObjectives()];
    
    problem_ = new NADIRProblem(problem_, ideal, index);
    
    particles_ = new SolutionSet(swarmSize_);
    best_ = new Solution[swarmSize_];
    leaders_ = new ModifiedCrowdingArchive(archiveSize_, problem_.getNumberOfObjectives());

    // Create comparators for dominance and crowding distance
    dominance_ = new DominanceComparator();
    crowdingDistanceComparator_ = new CrowdingDistanceComparator();
    distance_ = new Distance();

    // Create the speed_ vector
    speed_ = new double[swarmSize_][problem_.getNumberOfVariables()];


    deltaMax_ = new double[problem_.getNumberOfVariables()];
    deltaMin_ = new double[problem_.getNumberOfVariables()];
    for (int i = 0; i < problem_.getNumberOfVariables(); i++) {
      deltaMax_[i] = (problem_.getUpperLimit(i) -
        problem_.getLowerLimit(i)) / 2.0;
      deltaMin_[i] = -deltaMax_[i];
    } // for
  } // initParams 

  // Adaptive inertia 
  private double inertiaWeight(int iter, int miter, double wma, double wmin) {
    return wma; // - (((wma-wmin)*(double)iter)/(double)miter);
  } // inertiaWeight

  // constriction coefficient (M. Clerc)
  private double constrictionCoefficient(double c1, double c2) {
    double rho = c1 + c2;
    //rho = 1.0 ;
    if (rho <= 4) {
      return 1.0;
    } else {
      return 2 / (2 - rho - Math.sqrt(Math.pow(rho, 2.0) - 4.0 * rho));
    }
  } // constrictionCoefficient


  // velocity bounds
  private double velocityConstriction(double v, double[] deltaMax,
                                      double[] deltaMin, int variableIndex,
                                      int particleIndex) throws IOException {


    double result;

    double dmax = deltaMax[variableIndex];
    double dmin = deltaMin[variableIndex];

    result = v;

    if (v > dmax) {
      result = dmax;
    }

    if (v < dmin) {
      result = dmin;
    }

    return result;
  } // velocityConstriction

  /**
   * Update the speed of each particle
   * @throws JMException 
   */
  private void computeSpeed(int iter, int miter) throws JMException, IOException {
    double r1, r2, W, C1, C2;
    double wmax, wmin, deltaMax, deltaMin;
    XReal bestGlobal;

    for (int i = 0; i < swarmSize_; i++) {
    	XReal particle = new XReal(particles_.get(i)) ;
    	XReal bestParticle = new XReal(best_[i]) ;

      //Select a global best_ for calculate the speed of particle i, bestGlobal
      Solution one, two;
      int pos1 = PseudoRandom.randInt(0, leaders_.size() - 1);
      int pos2 = PseudoRandom.randInt(0, leaders_.size() - 1);
      one = leaders_.get(pos1);
      two = leaders_.get(pos2);

      if (crowdingDistanceComparator_.compare(one, two) < 1) {
        bestGlobal = new XReal(one);
      } else {
        bestGlobal = new XReal(two);
      //Params for velocity equation
      }
      r1 = PseudoRandom.randDouble(r1Min_, r1Max_);
      r2 = PseudoRandom.randDouble(r2Min_, r2Max_);
      C1 = PseudoRandom.randDouble(C1Min_, C1Max_);
      C2 = PseudoRandom.randDouble(C2Min_, C2Max_);
      W = PseudoRandom.randDouble(WMin_, WMax_);
      //
      wmax = WMax_;
      wmin = WMin_;

      for (int var = 0; var < particle.getNumberOfDecisionVariables(); var++) {
        //Computing the velocity of this particle 
        speed_[i][var] = velocityConstriction(constrictionCoefficient(C1, C2) *
          (inertiaWeight(iter, miter, wmax, wmin) *
          speed_[i][var] +
          C1 * r1 * (bestParticle.getValue(var) -
          particle.getValue(var)) +
          C2 * r2 * (bestGlobal.getValue(var) -
          particle.getValue(var))), deltaMax_, //[var],
          deltaMin_, //[var], 
          var,
          i);
      }
    }
  } // computeSpeed

  /**
   * Update the position of each particle
   * @throws JMException 
   */
  private void computeNewPositions() throws JMException {
    for (int i = 0; i < swarmSize_; i++) {
    	XReal particle = new XReal(particles_.get(i)) ;
      for (int var = 0; var < particle.getNumberOfDecisionVariables(); var++) {
      	particle.setValue(var, particle.getValue(var) +  speed_[i][var]) ;
      	
        if (particle.getValue(var) < problem_.getLowerLimit(var)) {
          particle.setValue(var, problem_.getLowerLimit(var));
          speed_[i][var] = speed_[i][var] * ChVel1_; //    
        }
        if (particle.getValue(var) > problem_.getUpperLimit(var)) {
          particle.setValue(var, problem_.getUpperLimit(var));
          speed_[i][var] = speed_[i][var] * ChVel2_; //   
        }
      }
    }
  } // computeNewPositions

  /**
   * Apply a mutation operator to some particles in the swarm
   * @throws JMException 
   */
  private void mopsoMutation(int actualIteration, int totalIterations) throws JMException {
    for (int i = 0; i < particles_.size(); i++) {
      if ( (i % 6) == 0)
        polynomialMutation_.execute(particles_.get(i)) ;
      //if (i % 3 == 0) { //particles_ mutated with a non-uniform mutation %3
      //  nonUniformMutation_.execute(particles_.get(i));
      //} else if (i % 3 == 1) { //particles_ mutated with a uniform mutation operator
      //  uniformMutation_.execute(particles_.get(i));
      //} else //particles_ without mutation
      //;
    }
  } // mopsoMutation

  /**   
   * Runs of the SMPSO algorithm.
   * @return a <code>SolutionSet</code> that is a set of non dominated solutions
   * as a result of the algorithm execution  
   * @throws JMException 
   */
  /*public SolutionSet execute() throws JMException, ClassNotFoundException {
    initParams();

    success_ = false;
    //->Step 1 (and 3) Create the initial population and evaluate
    for (int i = 0; i < swarmSize_; i++) {
      Solution particle = new Solution(problem_);
      problem_.evaluate(particle);
      problem_.evaluateConstraints(particle);
      particles_.add(particle);
    }

    //-> Step2. Initialize the speed_ of each particle to 0
    for (int i = 0; i < swarmSize_; i++) {
      for (int j = 0; j < problem_.getNumberOfVariables(); j++) {
        speed_[i][j] = 0.0;
      }
    }


    // Step4 and 5   
    for (int i = 0; i < particles_.size(); i++) {
      Solution particle = new Solution(particles_.get(i));
      leaders_.add(particle);
    }

    //-> Step 6. Initialize the memory of each particle
    for (int i = 0; i < particles_.size(); i++) {
      Solution particle = new Solution(particles_.get(i));
      best_[i] = particle;
    }

    //Crowding the leaders_
    distance_.crowdingDistanceAssignment(leaders_, problem_.getNumberOfObjectives());

    //-> Step 7. Iterations ..        
    while (iteration_ < maxIterations_) {
      try {
        //Compute the speed_
        computeSpeed(iteration_, maxIterations_);
      } catch (IOException ex) {
        Logger.getLogger(SMPSO.class.getName()).log(Level.SEVERE, null, ex);
      }

      //Compute the new positions for the particles_            
      computeNewPositions();

      //Mutate the particles_          
      mopsoMutation(iteration_, maxIterations_);

      //Evaluate the new particles_ in new positions
      for (int i = 0; i < particles_.size(); i++) {
        Solution particle = particles_.get(i);
        problem_.evaluate(particle);
      }

      //Actualize the archive          
      for (int i = 0; i < particles_.size(); i++) {
        Solution particle = new Solution(particles_.get(i));
        leaders_.add(particle);
      }

      //Actualize the memory of this particle
      for (int i = 0; i < particles_.size(); i++) {
        int flag = dominance_.compare(particles_.get(i), best_[i]);
        if (flag != 1) { // the new particle is best_ than the older remeber        
          Solution particle = new Solution(particles_.get(i));
          best_[i] = particle;
        }
      }

      //Assign crowding distance to the leaders_
      distance_.crowdingDistanceAssignment(leaders_,
        problem_.getNumberOfObjectives());
      iteration_++;
    }
    return this.leaders_;
  } // execute

*/  
  
  @Override
  protected SolutionSet getParetoFront() {
  	// TODO Auto-generated method stub
  	return this.leaders_;
  }

  
  
  
  
  
  @Override
  protected SolutionSet initializationAlgorithm() throws ClassNotFoundException,
  		JMException {
  	
	    initParams();

	    success_ = false;
	    //->Step 1 (and 3) Create the initial population and evaluate
	    
	    for (int i = 0; i < swarmSize_; i++) {
	      Solution particle = new Solution(problem_);	      
	      particles_.add(particle);
	    }

	    
	   // ((NADIRProblem)problem_).dimensionReduction(particles_);
	    
	    double temp[]   = ((NADIRProblem)problem_).getMinimumValues(particles_);
	    
	    for(int i=0; i < temp.length;  i++){
	    	ideal[i] = temp[i];
	    }
	    
	    ((NADIRProblem)problem_).directionVector(particles_,iteration_);
	    
	    for (int i = 0; i < swarmSize_; i++) {
	      Solution particle = particles_.get(i);
	      problem_.evaluate(particle);
	      problem_.evaluateConstraints(particle);
	    
	    }

	    
	    
	    for (int i = 0; i < swarmSize_; i++) {
	    	
	    	if(bestValueKnowed > particles_.get(i).getObjective(1)){
	    		bestValueKnowed =  particles_.get(i).getObjective(1);
	    	}
	    	
	    }
	    
	    
	    ((NADIRProblem)problem_).setBestValueKnowed(bestValueKnowed);
	    
	    //-> Step2. Initialize the speed_ of each particle to 0
	    for (int i = 0; i < swarmSize_; i++) {
	      for (int j = 0; j < problem_.getNumberOfVariables(); j++) {
	        speed_[i][j] = 0.0;
	      }
	    }


	    // Step4 and 5   
	    for (int i = 0; i < particles_.size(); i++) {
	      Solution particle = new Solution(particles_.get(i));
	      leaders_.add(particle);
	    }

	    //-> Step 6. Initialize the memory of each particle
	    for (int i = 0; i < particles_.size(); i++) {
	      Solution particle = new Solution(particles_.get(i));
	      best_[i] = particle;
	    }

	    //Crowding the leaders_
	    distance_.crowdingDistanceAssignment(leaders_, problem_.getNumberOfObjectives());

	    return this.leaders_;
	  
	  
  }

  
  
  
  
  
  
  private void localSearch() throws JMException{
	  
	  this.leaders_.sort(new ObjectiveComparator(0));
	  
	  int fitness1 = 0;
	  int fitness2 = 0;
	  
	  for(int i=0; i < this.leaders_.size()  ;i++){	
		  
		  int f1 = i+1;
		  int f2 = this.leaders_.size()-i+1;
		  fitness1 = Math.max(f1, f2);
		  this.leaders_.get(i).setFitness(fitness1);		  
	  }
	  
	  
	  
	  this.leaders_.sort(new ObjectiveComparator(1));
	  
	  for(int i=0; i < this.leaders_.size()  ;i++){	
		  int f1 = i+1;
		  int f2 = this.leaders_.size()-i+1;
		  fitness2 = Math.max(f1, f2);		  
		  
		  int fitness = Math.max(fitness2, (int)this.leaders_.get(i).getFitness());
		  
		  this.leaders_.get(i).setFitness(fitness);		  
	  }
	  
	  
	  
	  if(this.leaders_.size() > 20){
		  System.out.println();
	  }
	  
	  this.leaders_.sort(new FitnessComparator());
	  
	  Solution solution = new Solution(leaders_.get(0));
	  
	  this.polynomialMutation_.execute(solution);
	  
	  this.problem_.evaluate(solution);
	  
	  this.leaders_.add(solution);
	   
  }
  
  
/*private void localSearch() throws JMException{
	  
	  this.leaders_.sort(new ObjectiveComparator(0));
	  
	  int fitness1 = 0;
	  int fitness2 = 0;
	  
	  for(int i=0; i < this.leaders_.size()  ;i++){	
		  
		  int f1 = i+1;
		  int f2 = this.leaders_.size()-i+1;
		  fitness1 = Math.max(f1, f2);
		  this.leaders_.get(i).setFitness(fitness1);		  
	  }
	  
	  
	  
	  this.leaders_.sort(new ObjectiveComparator(1));
	  
	  for(int i=0; i < this.leaders_.size()  ;i++){	
		  int f1 = i+1;
		  int f2 = this.leaders_.size()-i+1;
		  fitness2 = Math.max(f1, f2);		  
		  
		  int fitness = Math.max(fitness2, (int)this.leaders_.get(i).getFitness());
		  
		  this.leaders_.get(i).setFitness(fitness);		  
	  }
	  
	  
	  
	  if(this.leaders_.size() > 20){
		  System.out.println();
	  }
	  
	  this.leaders_.sort(new FitnessComparator());
	  
	  Solution solution = new Solution(leaders_.get(0));
	  
	  this.polynomialMutation_.execute(solution);
	  
	  this.problem_.evaluate(solution);
	  
	  this.leaders_.add(solution);
	   
  }*/
  
  
  
  
  private void crossOver() throws JMException{
	  
	
	  this.leaders_.sort(new ObjectiveComparator(0));
	  
	  Solution parent1 = this.leaders_.get(0);
	  
	  this.leaders_.sort(new ObjectiveComparator(1));
	  
	  
	  Solution parent2 = this.leaders_.get(0);
	  
	  
	  double crossoverProbability_        = GlobalSettings.crossoverProbability_  ;
	  double mutationDistributionIndex_   = GlobalSettings.mutationDistributionIndex_ ;
	  double crossoverDistributionIndex_  = GlobalSettings.crossoverDistributionIndex_ ;
	  
	  HashMap parameters = new HashMap() ;
	    parameters.put("probability", crossoverProbability_) ;
	    parameters.put("distributionIndex", crossoverDistributionIndex_) ;
	    
	  
	Crossover  crossover = 
		CrossoverFactory.getCrossoverOperator("SBXCrossover", parameters);
	  
	Solution[] parents = new Solution[2];
	
	parents[0] = parent1;
	parents[1] = parent2;
	
	
	Solution[] offSpring = (Solution[])crossover.execute(parents);
	
	
	problem_.evaluate(offSpring[0]);
	problem_.evaluateConstraints(offSpring[0]);
	problem_.evaluate(offSpring[1]);
	problem_.evaluateConstraints(offSpring[1]);
	
	this.leaders_.add(offSpring[0]);
	this.leaders_.add(offSpring[1]);
	
  }
  
  
  
  
  
 
  
  
  
  

  private void crossOver2() throws JMException{
	  		
	  this.leaders_.sort(new ObjectiveComparator(0));
	  
	  SolutionSet population1 = new SolutionSet(this.leaders_.size());
	  SolutionSet population2 = new SolutionSet(this.leaders_.size());
	  
	  for(int i=0; i  < this.leaders_.size() ;i++){
		  population1.add(this.leaders_.get(i));
	  }
	  
	  
	  this.leaders_.sort(new ObjectiveComparator(1));
	  
	  for(int i=0; i  < this.leaders_.size() ;i++){
		  population2.add(this.leaders_.get(i));
	  }
	  
	  
	  double crossoverProbability_        = GlobalSettings.crossoverProbability_  ;	  
	  double crossoverDistributionIndex_  = GlobalSettings.crossoverDistributionIndex_ ;
	  
	  HashMap parameters = new HashMap() ;
	    parameters.put("probability", crossoverProbability_) ;
	    parameters.put("distributionIndex", crossoverDistributionIndex_) ;
	    
	  
	Crossover  crossover = 
		CrossoverFactory.getCrossoverOperator("SBXCrossover", parameters);
	  
	Solution[] parents = new Solution[2];
	
try{		
	for (int i = 0; i < (population1.size() / 2); i++) {
		
		
		
		parents[0] = population1.get(i);
		parents[1] = population2.get(i);	
		Solution[] offSpring = (Solution[])crossover.execute(parents);
		
		problem_.evaluate(offSpring[0]);
		problem_.evaluateConstraints(offSpring[0]);
		problem_.evaluate(offSpring[1]);
		problem_.evaluateConstraints(offSpring[1]);
		
		this.leaders_.add(offSpring[0]);
		this.leaders_.add(offSpring[1]);
		
	}
	
}catch (Exception e) {
	System.out.println();
}
	
  }

  
  
  @Override
  protected SolutionSet runIteration() throws JMException {
  	
	  
	  try {
	        //Compute the speed_
	        computeSpeed(iteration_, maxIterations_);
	      } catch (IOException ex) {
	        Logger.getLogger(SUBSMPSO.class.getName()).log(Level.SEVERE, null, ex);
	      }

	      //Compute the new positions for the particles_            
	      computeNewPositions();

	      //Mutate the particles_          
	      mopsoMutation(iteration_, maxIterations_);

	      double temp[] 
		                = ((NADIRProblem)problem_).getMinimumValues(particles_);
		       
		  for(int i=0; i < temp.length;  i++){
		    	ideal[i] = Math.min(ideal[i], temp[i]);
		  }
	      
		  if(iteration_ == 298){
			  System.out.println();
		  }
			  
			  
		  ((NADIRProblem)problem_).directionVector(particles_,iteration_);
	      
	      //Evaluate the new particles_ in new positions
	      for (int i = 0; i < particles_.size(); i++) {
	        Solution particle = particles_.get(i);
	        problem_.evaluate(particle);
	      }

	      crossOver();
	      
	      localSearch();
	      
	      for (int i = 0; i < swarmSize_; i++) {
		    	
		    	if(bestValueKnowed > particles_.get(i).getObjective(1)){
		    		bestValueKnowed = particles_.get(i).getObjective(1);
		    	}
		    	
		   }

	      ((NADIRProblem)problem_).setBestValueKnowed(bestValueKnowed);
	      
	      //Actualize the archive          
	      for (int i = 0; i < particles_.size(); i++) {
	        Solution particle = new Solution(particles_.get(i));
	        leaders_.add(particle);
	      }

	      
	      leaders_.sort(Collections.reverseOrder(new ObjectiveComparator(0)));
	      
	      if(this.leaders_.size() > (archiveSize_/2) ){	      
		      while(this.leaders_.size() > (archiveSize_/2)){
		    	  this.leaders_.remove(0);
		      }
	      }
	      
	      //Actualize the memory of this particle
	      for (int i = 0; i < particles_.size(); i++) {
	        int flag = dominance_.compare(particles_.get(i), best_[i]);
	        if (flag != 1) { // the new particle is best_ than the older remeber        
	          Solution particle = new Solution(particles_.get(i));
	          best_[i] = particle;
	        }else{
	        	
	        	/*if(best_[i].getObjective(1) > particles_.get(i).getObjective(1)){
	        	
	        		 Solution particle = new Solution(particles_.get(i));
	   	          	 best_[i] = particle;
	        		
	        	}
	        	*/
	        }
	      }

	      //Assign crowding distance to the leaders_
	      distance_.crowdingDistanceAssignment(leaders_,
	        problem_.getNumberOfObjectives());
	      iteration_++;

	      
	   /*   
	    leaders_.sort(new ObjectiveComparator(0));
	    double value1 =  leaders_.get(0).getObjective(0);
	    
	     leaders_.sort(new ObjectiveComparator(2)); 
	    double value2 =  leaders_.get(0).getObjective(2);*/
	      
	      if(estagnation()){
	    	  System.out.println();
	      }
	      
	    
	    
	      return this.leaders_;
	  
  }

  
  private double oldValue = 0.0;
  private int fails = 0;
  
  private boolean estagnation(){
	  	  
	  leaders_.sort(new ObjectiveComparator(0));
	  Solution solution1 = leaders_.get(0);
	  double value1 =  solution1.getObjective(0);
	    
	  leaders_.sort(new ObjectiveComparator(2));
	  Solution solution2 = leaders_.get(0);
	  double value2 =  solution2.getObjective(2);
	    
	  double value = -1;
	  boolean flag = false;
	  
	  if(value1 == 0.0 || value2 == 0.0){
		  
		  if(value1 == 0.0)
			  value = solution1.getObjective(1);
		  
		  if(value2 == 0.0)
			  value = solution2.getObjective(1);
		
		  flag = true;
	  }
	  
	  if(flag && (value == oldValue)){
		fails++;  
	  }else{
		  fails = 0;
	  }
	  
	  oldValue = value;
	  
	  if(fails == 10){
		  return true;
	  }else{
		  return false;
	  }
	  
	  
  }
  
  
  public void insertInExternArchive(SolutionSet leaders) throws JMException{
	  
	  
	  for (int i = 0; i < leaders.size(); i++) {
	        Solution solution = new Solution(leaders.get(i));
	        problem_.evaluate(solution);
	        leaders_.add(solution);
	 }

	  
	  leaders_.sort(Collections.reverseOrder(new ObjectiveComparator(0)));
      
      if(this.leaders_.size() > (archiveSize_/2) ){	      
	      while(this.leaders_.size() > (archiveSize_/2)){
	    	  this.leaders_.remove(0);
	      }
      }
      
	  
  }
  
  
  
  /** 
   * Gets the leaders of the SMPSO algorithm
   */
  public SolutionSet getLeader() {
	  
	SolutionSet solutionSet = new SolutionSet(leaders_.size());  
	  
	for(int i=0; i<leaders_.size();i++){
		solutionSet.add(leaders_.get(i));
	}
	  
    return solutionSet;
    
  }  // getLeader   




} // SMPSO
